﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LHSourceGenerator1 {
    [Generator(LanguageNames.CSharp)]
    public class GenValuePropertySourceGenerator : IIncrementalGenerator {
        /// <summary>
        /// 源生成器的主要
        /// </summary>
        /// <param name="context"></param>
        public void Initialize(IncrementalGeneratorInitializationContext context) {
            var nodes = context.SyntaxProvider
                .CreateSyntaxProvider(
                    predicate: static (node, _) => (node is PropertyDeclarationSyntax p && p.AttributeLists.Count > 0)
                    || (node is FieldDeclarationSyntax f && f.AttributeLists.Count > 0),
                    transform: static (ctx, _) => GetPropertyDeclarationWithAttribute(ctx)
                ).Where(static m => m is not null);

            var compilationAndProperties = context.CompilationProvider.Combine(nodes.Collect());

            context.RegisterSourceOutput(compilationAndProperties,
                static (spc, source) => Execute(source.Item1, source.Item2, spc));
        }

        /// <summary>
        /// 返回有相应注解标记的string类型的字段和属性，如果是字段，不许同时有CommunityToolkit的ObservableProperty注解
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private static SyntaxNode GetPropertyDeclarationWithAttribute(GeneratorSyntaxContext context) {
            if (context.Node is PropertyDeclarationSyntax propertyDeclaration) {
                var typeName = propertyDeclaration.Type.ToString();
                if (!typeName.Equals("string", System.StringComparison.OrdinalIgnoreCase))
                    return null;

                foreach (var attributeList in propertyDeclaration.AttributeLists) {
                    foreach (var attribute in attributeList.Attributes) {
                        if (context.SemanticModel.GetSymbolInfo(attribute).Symbol is not IMethodSymbol attributeSymbol)
                            continue;
                        var attributeSymbolName = attributeSymbol.ContainingType.ToDisplayString();
                        if (attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateDoubleValueAttribute", StringComparison.Ordinal) ||
                            attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateIntValueAttribute", StringComparison.Ordinal)) {
                            return propertyDeclaration;
                        }
                    }
                }
            } else if (context.Node is FieldDeclarationSyntax fieldDeclaration) {
                var typeName = fieldDeclaration.Declaration.Type.ToString();
                if (!typeName.Equals("string", System.StringComparison.OrdinalIgnoreCase))
                    return null;

                bool flag1 = false;
                bool flag2 = false;
                foreach (var attributeList in fieldDeclaration.AttributeLists) {
                    foreach (var attribute in attributeList.Attributes) {
                        if (context.SemanticModel.GetSymbolInfo(attribute).Symbol is not IMethodSymbol attributeSymbol)
                            continue;
                        var attributeSymbolName = attributeSymbol.ContainingType.ToDisplayString();
                        if (attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateDoubleValueAttribute", StringComparison.Ordinal) ||
                            attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateIntValueAttribute", StringComparison.Ordinal)) {
                            flag1 = true;
                        } else if (attributeSymbolName.Equals("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute", StringComparison.Ordinal)) {
                            flag2 = true;
                        }
                        if (flag1 & flag2)
                            return fieldDeclaration;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// 遍历满足要求的字段和属性，生成相应的代码
        /// </summary>
        /// <param name="compilation"></param>
        /// <param name="nodes"></param>
        /// <param name="context"></param>
        private static void Execute(Compilation compilation, IEnumerable<SyntaxNode> nodes, SourceProductionContext context) {
            if (!nodes.Any())
                return;

            // 按照所在的类对进行分组
            var groupedNodes = nodes.GroupBy(GetContainingClass);

            // 对每个类都要生成一个partial类
            foreach (var group in groupedNodes) {
                var classDeclaration = group.Key;
                if (classDeclaration == null)
                    continue;

                var semanticModel = compilation.GetSemanticModel(classDeclaration.SyntaxTree);
                var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol;
                if (classSymbol == null)
                    continue;

                // 命名空间名和类名
                var namespaceName = classSymbol.ContainingNamespace.ToDisplayString();
                var className = classSymbol.Name;

                // 源代码
                var sourceBuilder = new StringBuilder();

                sourceBuilder.AppendLine($"// <auto-generated/>");
                sourceBuilder.AppendLine($"using System;");
                sourceBuilder.AppendLine();
                sourceBuilder.AppendLine($"namespace {namespaceName}");
                sourceBuilder.AppendLine("{");
                sourceBuilder.AppendLine($"    partial class {className}");
                sourceBuilder.AppendLine("    {");

                // 对筛选后的字段和属性进行代码生成
                foreach (var node in group) {
                    string symbolName = GetSymbolName(node);
                    if (symbolName == null) continue;

                    // 属性节点
                    if (node is PropertyDeclarationSyntax propertyDeclaration) {
                        var computedPropertyName = $"{symbolName}Value";
                        // 以第一个注解为准
                        foreach (var attributeList in propertyDeclaration.AttributeLists) {
                            foreach (var attribute in attributeList.Attributes) {
                                if (semanticModel.GetSymbolInfo(attribute).Symbol is not IMethodSymbol attributeSymbol)
                                    continue;
                                var attributeSymbolName = attributeSymbol.ContainingType.ToDisplayString();
                                if (attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateDoubleValueAttribute", StringComparison.Ordinal)) {
                                    sourceBuilder.AppendLine($@"
    public double? {computedPropertyName}{{
        get{{
            if(double.TryParse({symbolName},out var resValue))
                return resValue;
            return null;
        }}
        set{{
            {symbolName}=value.ToString();
        }}
    }}");
                                    break;
                                } else if (attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateIntValueAttribute", StringComparison.Ordinal)) {
                                    sourceBuilder.AppendLine($@"
    public int? {computedPropertyName}{{
        get{{
            if(int.TryParse({symbolName},out var resValue))
                return resValue;
            return null;
        }}
        set{{
            {symbolName}=value.ToString();
        }}
    }}");
                                    break;
                                }
                            }
                        }
                    }
                    // 字段节点
                    else if (node is FieldDeclarationSyntax fieldDeclaration) {
                        // 获取名称
                        var propertyName = GetPropertyName(symbolName);
                        var computedPropertyName = $"{propertyName}Value";
                        // 以第一个注解为准
                        foreach (var attributeList in fieldDeclaration.AttributeLists) {
                            foreach (var attribute in attributeList.Attributes) {
                                if (semanticModel.GetSymbolInfo(attribute).Symbol is not IMethodSymbol attributeSymbol)
                                    continue;
                                var attributeSymbolName = attributeSymbol.ContainingType.ToDisplayString();
                                if (attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateDoubleValueAttribute", StringComparison.Ordinal)) {
                                    sourceBuilder.AppendLine($@"
    public double? {computedPropertyName}{{
        get{{
            if(double.TryParse({propertyName},out var resValue))
                return resValue;
            return null;
        }}
        set{{
            {propertyName}=value.ToString();
        }}
    }}");
                                    break;
                                } else if (attributeSymbolName.Equals("SourceGeneratorAttribute.GenerateIntValueAttribute", StringComparison.Ordinal)) {
                                    sourceBuilder.AppendLine($@"
    public int? {computedPropertyName}{{
        get{{
            if(int.TryParse({propertyName},out var resValue))
                return resValue;
            return null;
        }}
        set{{
            {propertyName}=value.ToString();
        }}
    }}");
                                    break;
                                }
                            }
                        }
                    }
                }

                sourceBuilder.AppendLine("      }");
                sourceBuilder.AppendLine("}");

                context.AddSource($"{className}_GenValue.g.cs",
                    SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
            }
        }

        private static ClassDeclarationSyntax GetContainingClass(SyntaxNode node) {
            return node.Parent as ClassDeclarationSyntax;
        }

        /// <summary>
        /// 根据字段名获得属性名（_fieldName,m_fieldName,fieldName）
        /// </summary>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        private static string GetPropertyName(string fieldName) {
            if (string.IsNullOrWhiteSpace(fieldName))
                return null;

            string propertyName = fieldName;
            if (fieldName.StartsWith("_")) {
                if (fieldName.Length == 1)
                    return null;
                propertyName = fieldName.Substring(1);
            } else if (fieldName.StartsWith("m_")) {
                if (fieldName.Length == 2)
                    return null;
                propertyName = fieldName.Substring(2);
            }
            return char.ToUpper(propertyName[0]) + propertyName.Substring(1);
        }

        /// <summary>
        /// 获得属性和字段的符号名
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static string GetSymbolName(SyntaxNode node) {
            if (node is FieldDeclarationSyntax fieldDeclaration) {
                foreach (var variable in fieldDeclaration.Declaration.Variables) {
                    return variable.Identifier.ValueText;
                }
            } else if (node is PropertyDeclarationSyntax propertyDeclaration) {
                return propertyDeclaration.Identifier.ValueText;
            }
            throw new ArgumentException("Unsupported syntax node type", nameof(node));
        }
    }
}

